/*********************************************************************************
 *                                                                               *
 * The MIT License (MIT)                                                         *
 *                                                                               *
 * Copyright (c) 2015-2022 aoju.org and other contributors.                      *
 *                                                                               *
 * Permission is hereby granted, free of charge, to any person obtaining a copy  *
 * of this software and associated documentation files (the "Software"), to deal *
 * in the Software without restriction, including without limitation the rights  *
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell     *
 * copies of the Software, and to permit persons to whom the Software is         *
 * furnished to do so, subject to the following conditions:                      *
 *                                                                               *
 * The above copyright notice and this permission notice shall be included in    *
 * all copies or substantial portions of the Software.                           *
 *                                                                               *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR    *
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,      *
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE   *
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER        *
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, *
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN     *
 * THE SOFTWARE.                                                                 *
 *                                                                               *
 ********************************************************************************/
package org.aoju.bus.core.map;

import org.aoju.bus.core.lang.Optional;
import org.aoju.bus.core.toolkit.CollKit;
import org.aoju.bus.core.toolkit.ObjectKit;

import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Function;

/**
 * 基于多个{@link TreeEntry}构成的、彼此平行的树结构构成的森林集合。
 *
 * @param <K> 键类型
 * @param <V> 值类型
 * @author Kimi Liu
 * @since Java 17+
 */
public interface ForestMap<K, V> extends Map<K, TreeEntry<K, V>> {

    /**
     * 添加一个节点，效果等同于 {@code putNode(key, node.getValue())}
     * <ul>
     *     <li>若key对应节点不存在，则以传入的键值创建一个新的节点；</li>
     *     <li>若key对应节点存在，则将该节点的值替换为{@code node}指定的值；</li>
     * </ul>
     *
     * @param key  节点的key值
     * @param node 节点
     * @return 节点，若key已有对应节点，则返回具有旧值的节点，否则返回null
     * @see #putNode(Object, Object)
     */
    @Override
    default TreeEntry<K, V> put(final K key, final TreeEntry<K, V> node) {
        return putNode(key, node.getValue());
    }

    /**
     * 批量添加节点，若节点具有父节点或者子节点，则一并在当前实例中引入该关系
     *
     * @param treeEntryMap 节点集合
     */
    @Override
    default void putAll(final Map<? extends K, ? extends TreeEntry<K, V>> treeEntryMap) {
        if (CollKit.isEmpty(treeEntryMap)) {
            return;
        }
        treeEntryMap.forEach((k, v) -> {
            if (v.hasParent()) {
                final TreeEntry<K, V> parent = v.getDeclaredParent();
                putLinkedNodes(parent.getKey(), parent.getValue(), v.getKey(), v.getValue());
            } else {
                putNode(v.getKey(), v.getValue());
            }
        });
    }

    /**
     * 批量添加节点
     *
     * @param <C>                集合类型
     * @param values             要添加的值
     * @param keyGenerator       从值中获取key的方法
     * @param parentKeyGenerator 从值中获取父节点key的方法
     * @param ignoreNullNode     是否获取到的key为null的子节点/父节点
     */
    default <C extends Collection<V>> void putAllNode(
            final C values, final Function<V, K> keyGenerator, final Function<V, K> parentKeyGenerator, final boolean ignoreNullNode) {
        if (CollKit.isEmpty(values)) {
            return;
        }
        values.forEach(v -> {
            final K key = keyGenerator.apply(v);
            final K parentKey = parentKeyGenerator.apply(v);

            // 不忽略keu为null节点
            final boolean hasKey = ObjectKit.isNotNull(key);
            final boolean hasParentKey = ObjectKit.isNotNull(parentKey);
            if (!ignoreNullNode || (hasKey && hasParentKey)) {
                linkNodes(parentKey, key);
                get(key).setValue(v);
                return;
            }

            // 父子节点的key都为null
            if (!hasKey && !hasParentKey) {
                return;
            }

            // 父节点key为null
            if (hasKey) {
                putNode(key, v);
                return;
            }

            // 子节点key为null
            putNode(parentKey, null);
        });
    }

    /**
     * 添加一个节点
     * <ul>
     *     <li>若key对应节点不存在，则以传入的键值创建一个新的节点；</li>
     *     <li>若key对应节点存在，则将该节点的值替换为{@code node}指定的值；</li>
     * </ul>
     *
     * @param key   节点的key
     * @param value 节点的value
     * @return 节点，若key已有对应节点，则返回具有旧值的节点，否则返回null
     */
    TreeEntry<K, V> putNode(K key, V value);

    /**
     * 同时添加父子节点：
     * <ul>
     *     <li>若{@code parentKey}或{@code childKey}对应的节点不存在，则会根据键值创建一个对应的节点；</li>
     *     <li>若{@code parentKey}或{@code childKey}对应的节点存在，则会更新对应节点的值；</li>
     * </ul>
     * 该操作等同于：
     * <pre>{@code
     *     putNode(parentKey, parentValue);
     *     putNode(childKey, childValue);
     *     linkNodes(parentKey, childKey);
     * }</pre>
     *
     * @param parentKey   父节点的key
     * @param parentValue 父节点的value
     * @param childKey    子节点的key
     * @param childValue  子节点的值
     */
    default void putLinkedNodes(final K parentKey, final V parentValue, final K childKey, final V childValue) {
        putNode(parentKey, parentValue);
        putNode(childKey, childValue);
        linkNodes(parentKey, childKey);
    }

    /**
     * 添加子节点，并为子节点指定父节点：
     * <ul>
     *     <li>若{@code parentKey}或{@code childKey}对应的节点不存在，则会根据键值创建一个对应的节点；</li>
     *     <li>若{@code parentKey}或{@code childKey}对应的节点存在，则会更新对应节点的值；</li>
     * </ul>
     *
     * @param parentKey  父节点的key
     * @param childKey   子节点的key
     * @param childValue 子节点的值
     */
    void putLinkedNodes(K parentKey, K childKey, V childValue);

    /**
     * 为集合中的指定的节点建立父子关系
     *
     * @param parentKey 父节点的key
     * @param childKey  子节点的key
     */
    default void linkNodes(final K parentKey, final K childKey) {
        linkNodes(parentKey, childKey, null);
    }

    /**
     * 为集合中的指定的节点建立父子关系
     *
     * @param parentKey 父节点的key
     * @param childKey  子节点的key
     * @param consumer  对父节点和子节点的操作，允许为null
     */
    void linkNodes(K parentKey, K childKey, BiConsumer<TreeEntry<K, V>, TreeEntry<K, V>> consumer);

    /**
     * 若{@code parentKey}或{@code childKey}对应节点都存在，则移除指定该父节点与其直接关联的指定子节点间的引用关系
     *
     * @param parentKey 父节点的key
     * @param childKey  子节点
     */
    void unlinkNode(K parentKey, K childKey);

    /**
     * 获取指定节点所在树结构的全部树节点
     * 比如：存在 a -&gt; b -&gt; c 的关系，则输入 a/b/c 都将返回 a, b, c
     *
     * @param key 指定节点的key
     * @return 节点
     */
    default Set<TreeEntry<K, V>> getTreeNodes(final K key) {
        final TreeEntry<K, V> target = get(key);
        if (ObjectKit.isNull(target)) {
            return Collections.emptySet();
        }
        final Set<TreeEntry<K, V>> results = CollKit.newLinkedHashSet(target.getRoot());
        CollKit.addAll(results, target.getRoot().getChildren().values());
        return results;
    }

    /**
     * 获取以指定节点作为叶子节点的树结构，然后获取该树结构的根节点
     * 比如：存在 a -&gt; b -&gt; c 的关系，则输入 a/b/c 都将返回 a
     *
     * @param key 指定节点的key
     * @return 节点
     */
    default TreeEntry<K, V> getRootNode(final K key) {
        return Optional.ofNullable(get(key))
                .map(TreeEntry::getRoot)
                .orElse(null);
    }

    /**
     * 获取指定节点的直接父节点
     * 比如：若存在 a -&gt; b -&gt; c 的关系，此时输入 a 将返回 null，输入 b 将返回 a，输入 c 将返回 b
     *
     * @param key 指定节点的key
     * @return 节点
     */
    default TreeEntry<K, V> getDeclaredParentNode(final K key) {
        return Optional.ofNullable(get(key))
                .map(TreeEntry::getDeclaredParent)
                .orElse(null);
    }

    /**
     * 获取以指定节点作为叶子节点的树结构，然后获取该树结构中指定节点的指定父节点
     *
     * @param key       指定节点的key
     * @param parentKey 指定父节点key
     * @return 节点
     */
    default TreeEntry<K, V> getParentNode(final K key, final K parentKey) {
        return Optional.ofNullable(get(key))
                .map(t -> t.getParent(parentKey))
                .orElse(null);
    }

    /**
     * 获取以指定节点作为叶子节点的树结构，然后确认该树结构中当前节点是否存在指定父节点
     *
     * @param key       指定节点的key
     * @param parentKey 指定父节点的key
     * @return 是否
     */
    default boolean containsParentNode(final K key, final K parentKey) {
        return Optional.ofNullable(get(key))
                .map(m -> m.containsParent(parentKey))
                .orElse(false);
    }

    /**
     * 获取指定节点的值
     *
     * @param key 节点的key
     * @return 节点值，若节点不存在，或节点值为null都将返回null
     */
    default V getNodeValue(final K key) {
        return Optional.ofNullable(get(key))
                .map(TreeEntry::getValue)
                .get();
    }

    /**
     * 判断以该父节点作为根节点的树结构中是否具有指定子节点
     *
     * @param parentKey 父节点
     * @param childKey  子节点
     * @return 是否
     */
    default boolean containsChildNode(final K parentKey, final K childKey) {
        return Optional.ofNullable(get(parentKey))
                .map(m -> m.containsChild(childKey))
                .orElse(false);
    }

    /**
     * 获取指定父节点直接关联的子节点
     * 比如：若存在 a -&gt; b -&gt; c 的关系，此时输入 b 将返回 c，输入 a 将返回 b
     *
     * @param key key
     * @return 节点
     */
    default Collection<TreeEntry<K, V>> getDeclaredChildNodes(final K key) {
        return Optional.ofNullable(get(key))
                .map(TreeEntry::getDeclaredChildren)
                .map(Map::values)
                .orElseGet(Collections::emptyList);
    }

    /**
     * 获取指定父节点的全部子节点
     * 比如：若存在 a -&gt; b -&gt; c 的关系，此时输入 b 将返回 c，输入 a 将返回 b，c
     *
     * @param key key
     * @return 该节点的全部子节点
     */
    default Collection<TreeEntry<K, V>> getChildNodes(final K key) {
        return Optional.ofNullable(get(key))
                .map(TreeEntry::getChildren)
                .map(Map::values)
                .orElseGet(Collections::emptyList);
    }

}
